//package com.tianji.promotion.service.impl;
//
//import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
//import com.tianji.common.domain.dto.PageDTO;
//import com.tianji.common.exceptions.BadRequestException;
//import com.tianji.common.exceptions.BizIllegalException;
//import com.tianji.common.utils.UserContext;
//import com.tianji.promotion.domain.po.Coupon;
//import com.tianji.promotion.domain.po.ExchangeCode;
//import com.tianji.promotion.domain.po.UserCoupon;
//import com.tianji.promotion.domain.query.UserCouponQuery;
//import com.tianji.promotion.domain.utils.CodeUtil;
//import com.tianji.promotion.domain.utils.RedisLock;
//import com.tianji.promotion.domain.vo.CouponVO;
//import com.tianji.promotion.enums.CouponStatus;
//import com.tianji.promotion.enums.ExchangeCodeStatus;
//import com.tianji.promotion.mapper.CouponMapper;
//import com.tianji.promotion.mapper.UserCouponMapper;
//import com.tianji.promotion.service.IExchangeCodeService;
//import com.tianji.promotion.service.IUserCouponService;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.aop.framework.AopContext;
//import org.springframework.data.redis.core.StringRedisTemplate;
//import org.springframework.stereotype.Service;
//import org.springframework.transaction.annotation.Transactional;
//
//import javax.annotation.Resource;
//import java.time.LocalDateTime;
//import java.util.concurrent.TimeUnit;
//
///**
// * <p>
// * 用户领取优惠券的记录，是真正使用的优惠券信息 服务实现类
// * </p>
// *
// * @author Yandel
// * @since 2025-08-17
// */
//@Slf4j
//@Service
//public class UserCouponRedisServiceImpl extends ServiceImpl<UserCouponMapper, UserCoupon> implements IUserCouponService {
//
//    @Resource
//    private CouponMapper couponMapper;//不能直接注入couponService,避免循环依赖
//
//    @Resource
//    private IExchangeCodeService exchangeCodeService;
//
//    @Resource
//    private StringRedisTemplate stringRedisTemplate;
//
//    @Override
////    @Transactional
//    public void receiveCoupon(Long couponId) {
//        if (couponId == null) {
//            throw new BadRequestException("优惠券ID不能为空");
//        }
//        Coupon coupon = couponMapper.selectById(couponId);
//        //校验优惠券是否存在，不存在无法领取
//        if (coupon == null) {
//            throw new BadRequestException("优惠券不存在");
//        }
//        //校验优惠券的发放时间，是不是正在发放中
//        if (coupon.getStatus() != CouponStatus.ISSUING) {
//            throw new BadRequestException("优惠券状态错误");
//        }
//        LocalDateTime now = LocalDateTime.now();
//        if (now.isBefore(coupon.getIssueBeginTime()) || now.isAfter(coupon.getIssueEndTime())) {
//            throw new BadRequestException("优惠券已过期或者发放时间已过");
//        }
//        //校验优惠券剩余库存是否充足
//        if (coupon.getTotalNum() <= 0 || coupon.getIssueNum() >= coupon.getTotalNum()) {
//            throw new BadRequestException("优惠券库存不足");
//        }
//        //获取当前用户 对该优惠券 已领数量  user_coupon   统计数量
// /*       Long userId = UserContext.getUser();
//        Integer count = this.lambdaQuery()
//                .eq(UserCoupon::getUserId, userId)
//                .eq(UserCoupon::getCouponId, couponId)
//                .count();
//        //校验优惠券的每人限领数量
//        if (count != null && count >= coupon.getUserLimit()) {
//            throw new BadRequestException("优惠券已领完");
//        }
//
//        //已发的优惠券+1
//        couponMapper.incrIssueNum(coupon.getId());//采用这种方式，会存在并发问题，后期考虑用乐观锁解决
//
//        //生成用户券
//        saveUserCoupon(coupon, userId);*/ //提出公共函数
//        Long userId = UserContext.getUser();
//
//        //直接使用synchronized锁 - 解决并发问题
///*        synchronized (userId.toString().intern()){//细度锁，锁的范围缩小，只锁定当前用户
//            checkAndCreateCoupon(userId, coupon,null);
//        }*/
//        //使用synchronized锁+AOP代理对象 -解决并发+事务问题
///*        synchronized (userId.toString().intern()) {//细度锁，锁的范围缩小，只锁定当前用户
//            IUserCouponService userCouponService = (IUserCouponService) AopContext.currentProxy();
////            checkAndCreateCoupon(userId, coupon,null);//该写法是调用原对象的方法【无事务调用有事务的】
//            userCouponService.checkAndCreateCoupon(userId, coupon, null);//这种写法是调用代理对象的方法，方法是事务处理的
//        }*/
//        //使用Redis分布式锁 + AOP代理对象 -解决并发+事务问题
//        String key = "lock:coupon:uid" + userId;
//        RedisLock redisLock = new RedisLock(key, stringRedisTemplate);
//        try {
//            boolean isLock = redisLock.tryLock(5, TimeUnit.SECONDS);
//            if (!isLock) {//isLock-false
//                throw new BizIllegalException("操作太频繁了~");
//            }
//            IUserCouponService userCouponService = (IUserCouponService) AopContext.currentProxy();
////            checkAndCreateCoupon(userId, coupon,null);//该写法是调用原对象的方法【无事务调用有事务的】
//            userCouponService.checkAndCreateCoupon(userId, coupon, null);//这种写法是调用代理对象的方法，方法是事务处理的
//
//        } finally {
//            redisLock.unlock();
//        }
//    }
//
//    @Transactional
//    public void checkAndCreateCoupon(Long userId, Coupon coupon, Long serialNum) {
//        //Long类型-128~127之间是同一个对象     超过该区间则是不同的对象
//        //Long.toString方法底层是newString，所以还是不同的对象
//        //Long.toString.intern（）intern方法是强制从常量池中取字符串
///*        synchronized (userId.toString().intern()){//用户限领数量判断是针对单个用户的，因此锁的范围不需要是整个方法，只要锁定某个用户即可
//            //1/获取当前用户 对该优惠券 已领数量  user_coupon   统计数量
//            Integer count = this.lambdaQuery()
//                    .eq(UserCoupon::getUserId, userId)
//                    .eq(UserCoupon::getCouponId, coupon.getId())
//                    .count();
//            //2、校验优惠券的每人限领数量
//            if (count != null && count >= coupon.getUserLimit()) {
//                throw new BadRequestException("优惠券已领完");
//            }
//            //已发的优惠券+1
//            couponMapper.incrIssueNum(coupon.getId());//采用这种方式
//            //3、生成用户券
//            saveUserCoupon(coupon, userId);
//
//            //4、更新兑换码的状态
//            if (serialNum != null) {
//                exchangeCodeService.lambdaUpdate()
//                        .set(ExchangeCode::getUserId, userId)
//                        .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
//                        .eq(ExchangeCode::getId, serialNum)
//                        .update();
//            }
//        }*/
//        //1/获取当前用户 对该优惠券 已领数量  user_coupon   统计数量
//        Integer count = this.lambdaQuery()
//                .eq(UserCoupon::getUserId, userId)
//                .eq(UserCoupon::getCouponId, coupon.getId())
//                .count();
//        //2、校验优惠券的每人限领数量
//        if (count != null && count >= coupon.getUserLimit()) {
//            throw new BadRequestException("优惠券已领完");
//        }
//        //已发的优惠券+1
//        couponMapper.incrIssueNum(coupon.getId());//采用这种方式
//        //3、生成用户券
//        saveUserCoupon(coupon, userId);
//
//        //4、更新兑换码的状态
//        if (serialNum != null) {
//            exchangeCodeService.lambdaUpdate()
//                    .set(ExchangeCode::getUserId, userId)
//                    .set(ExchangeCode::getStatus, ExchangeCodeStatus.USED)
//                    .eq(ExchangeCode::getId, serialNum)
//                    .update();
//        }
////        throw new RuntimeException("测试异常");
//    }
//
//
//    /**
//     * 生成用户券
//     *
//     * @param coupon
//     * @param userId
//     */
//    private void saveUserCoupon(Coupon coupon, Long userId) {
//        // 1.基本信息
//        UserCoupon uc = new UserCoupon();
//        uc.setUserId(userId);
//        uc.setCouponId(coupon.getId());
//
//        // 2.有效期信息
//        LocalDateTime termBeginTime = coupon.getTermBeginTime();// 优惠券有效期开始时间
//        LocalDateTime termEndTime = coupon.getTermEndTime();// 优惠券有效期结束时间
//        if (termBeginTime == null && termEndTime == null) {
//            termBeginTime = LocalDateTime.now();
//            termEndTime = termBeginTime.plusDays(coupon.getTermDays());//表示当前时间加优惠券有效日期
//        }
//        uc.setTermBeginTime(termBeginTime);
//        uc.setTermEndTime(termEndTime);
//
//        // 3.保存
//        this.save(uc);
//    }
//
//
//
//    @Override
//    @Transactional
//    public void exchangeCoupon(String code) {
//        //1、校验并解析兑换码
//        if (code == null) {
//            throw new BadRequestException("请输入兑换码");
//        }
//        //2、解析兑换码得到自增Id
//        long serialNum = CodeUtil.parseCode(code);//自增id 这是使用自增id的生成的兑换码
//
//        log.info("自增id：{}", serialNum);
//        //3、判断兑换码是否已兑换 采用 Redis的bitmap setbit   key offset 1
//        Boolean result = exchangeCodeService.updateExchaingeCodeMark(serialNum, true);
//        if (result) {
//            //说明兑换码已经被兑换了
//            throw new BizIllegalException("兑换码已兑换");
//        }
//        try {//为什么要使用try catch   因为防止现成的兑换码因执行下面的代码出现异常后影响兑换码的作用
//            //4.判断兑换码是否存在根据自增id查询主键查询
//            ExchangeCode exchangeCode = exchangeCodeService.getById(serialNum);
//            if (exchangeCode == null) {
//                throw new BizIllegalException("兑换码不存在~");
//            }
//            //5.判断是否过期
//            LocalDateTime now = LocalDateTime.now();
//            LocalDateTime expiredTime = exchangeCode.getExpiredTime();
//            if (now.isAfter(expiredTime)) {
//                throw new BizIllegalException("兑换码已过期~");
//            }
//            //校验并生成优惠券
//            Long userId = UserContext.getUser();
//            //查询优惠券信息
//            Coupon coupon = couponMapper.selectById(exchangeCode.getExchangeTargetId());
//            if (coupon == null) {
//                throw new BizIllegalException("优惠券不存在~");
//            }
//            //校验并生成优惠券
//            checkAndCreateCoupon(userId, coupon, serialNum);
//        } catch (Exception e) {
//            //10.将兑换码的状态重置
//            exchangeCodeService.updateExchaingeCodeMark(serialNum, false);
//            throw e;
//        }
//    }
//
//
//    @Override
//    public PageDTO<CouponVO> queryUserCoupons(UserCouponQuery query) {
//        return null;
//    }
//}
